package com.ygp.asyn.service;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.handler.WriteHandler;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ygp.asyn.config.CommonConfig;
import com.ygp.asyn.entity.*;
import com.ygp.asyn.enums.*;
import com.ygp.asyn.util.FileUtils;
import com.ygp.asyn.util.HttpUtil;
import com.ygp.asyn.util.MulThreadUtil;
import com.ygp.asyn.util.TimeUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;

import java.io.ByteArrayOutputStream;
import java.lang.reflect.Field;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.function.Function;

@Slf4j
public class AsyncDownloadCenter {


    //@Resource
    private CommonConfig commonConf;

    private RpcAsyncDownloadService rpcAsyncDownloadService;

    /**
     * 默认最大行数
     */
    public final static int MAX_LINE = 20000;

    /**
     * 默认文件失效时间（天）
     */
    public final static int EXPIRE_TM = 7;

    /**
     * 默认超时下载时间 分钟
     */
    public final static int downOverMinute = 10;

    public final static int downingCnt = 10;

    public final static int copyMinute = 30;

    public AsyncDownloadCenter(CommonConfig commonConf) {
        this.commonConf = commonConf;
    }

    public void setRpcAsyncDownloadService(RpcAsyncDownloadService rpcAsyncDownloadService) {
        this.rpcAsyncDownloadService = rpcAsyncDownloadService;
    }

    public <T, V, E> Result<E> executeRequestPage(Function<T, List<V>> function, T params, List<List<String>> head, String empCode, String fileName, String sheetName, DownCopyEnum dbEnum) {
        return executeRequestPage(function, params, null, head, empCode, fileName, sheetName, dbEnum, AppEnum.ALL);
    }

    public <T, V, E> Result<E> executeRequestPageSource(Function<T, List<V>> function, T params, List<List<String>> head, String empCode, String fileName, String sheetName, DownCopyEnum dbEnum, AppEnum appEnum) {
        return executeRequestPage(function, params, null, head, empCode, fileName, sheetName, dbEnum, appEnum);
    }

    public <T, V, E> Result<E> executeRequestPage(Function<T, List<V>> function, T params, Class<V> clazz, String empCode, String fileName, String sheetName, DownCopyEnum dbEnum) {
        return executeRequestPage(function, params, clazz, null, empCode, fileName, sheetName, dbEnum, AppEnum.ALL);
    }

    public <T, V, E> Result<E> executeRequestPageSource(Function<T, List<V>> function, T params, Class<V> clazz, String empCode, String fileName, String sheetName, DownCopyEnum dbEnum, AppEnum appEnum) {
        return executeRequestPage(function, params, clazz, null, empCode, fileName, sheetName, dbEnum, appEnum);
    }

    public <V, E> Result<E> executeRequestPage(List<Callable<List<V>>> callables, Class<V> clazz, String empCode, String fileName, DownCopyEnum dbEnum) {
        return executeRequestPage(callables, clazz, null, empCode, fileName, dbEnum, AppEnum.ALL);
    }

    public <V, E> Result<E> executeRequestPageSource(List<Callable<List<V>>> callables, Class<V> clazz, String empCode, String fileName, DownCopyEnum dbEnum, AppEnum appEnum) {
        return executeRequestPage(callables, clazz, null, empCode, fileName, dbEnum, appEnum);
    }

    public <T, V, E> Result<E> executeRequest(Function<T, List<V>> function, T params, List<List<String>> head, String empCode, String fileName, DownCopyEnum dbEnum) {
        return executeRequest(function, params, null, head, empCode, fileName, dbEnum, AppEnum.ALL);
    }

    public <T, V, E> Result<E> executeRequest(Function<T, List<V>> function, T params, Class<V> clazz, String empCode, String fileName, List<String> includeColumnNames, DownCopyEnum dbEnum) {
        return executeRequest(function, params, clazz, null, empCode, fileName, includeColumnNames, dbEnum);
    }

    public <T, V, E> Result<E> executeRequest(Function<T, List<V>> function, T params, Class<V> clazz, String empCode, String fileName, DownCopyEnum dbEnum) {
        return executeRequest(function, params, clazz, null, empCode, fileName, dbEnum, AppEnum.ALL);
    }

    public <T, V, E> Result<E> executeRequestSource(Function<T, List<V>> function, T params, List<List<String>> head, String empCode, String fileName, DownCopyEnum dbEnum, AppEnum appEnum) {
        return executeRequest(function, params, null, head, empCode, fileName, dbEnum, appEnum);
    }

    public <T, V, E> Result<E> executeRequestSource(Function<T, List<V>> function, T params, Class<V> clazz, String empCode, String fileName, DownCopyEnum dbEnum, AppEnum appEnum) {
        return executeRequest(function, params, clazz, null, empCode, fileName, dbEnum, appEnum);
    }

    private <T, V, E> Result<E> executeRequestPage(Function<T, List<V>> function, T params, Class<V> clazz, List<List<String>> head, String empCode, String fileName, String sheetName, DownCopyEnum dbEnum, AppEnum appEnum) {
        try {
            //更新超时状态
            updateOverTm();
            //当前下载业务支持&&总控支持&&数据库有满足条件的文件 才会执行copy
            if (dbEnum.getCopy() == 1 && copyAll(dbEnum, empCode, fileName)) {
                return Result.success();
            }
            if (judgeConcurrency()) {
                return Result.fail(DownloadReEnum.DOWNLOAD_TOO_MUCH.getMsg());
            }
            //是否重复下载
            DownloadEntity downEntity = new DownloadEntity();
            downEntity.setUserCode(empCode);
            downEntity.setFileName(fileName);
            downEntity.setStartTm(TimeUtil.nowToStr(TimeFormatEnum.YYYY_2));
            downEntity.setStatus(DownloadReEnum.DOWNLOADING.getStatus());
            downEntity.setBusinessCopy(dbEnum.getCopy());
            downEntity.setSource(appEnum.getApp());
            //同一个人存在同名文件在下载则禁止下载，实时也限制
            if (!selectDowningRepeat(downEntity)) {
                return Result.fail(DownloadReEnum.DOWNLOADING.getMsg());
            }
            //存储信息
            sendDowningStatus(downEntity);
            //异步执行,指定超时时间,超时则更新超时状态，成功则更新相应的信息
            MulThreadUtil.excAsyncInTime(
                    params,
                    param -> fileAndSendToOssPage(downEntity, function, param, clazz, head, fileName, sheetName),
                    this::sendDownedStatus,
                    () -> updateOverTmById(downEntity.getId()),
                    Duration.ofMinutes(getDownOverMinute())
            );
            return Result.success();
        } catch (Exception e) {
            log.error("executeRequest error params:{} ex:", params, e);
            return Result.fail();
        }
    }

    /**
     * @param callables ;
     * @param clazz     ;
     * @param head      ;
     * @param empCode   ;
     * @param fileName  ;
     * @param dbEnum    ;
     * @param <V>       ;
     * @param <E>       ;`
     * @return ;
     */
    private <V, E> Result<E> executeRequestPage(List<Callable<List<V>>> callables, Class<V> clazz, List<List<String>> head, String empCode, String fileName, DownCopyEnum dbEnum, AppEnum appEnum) {
        try {
            //更新超时状态
            updateOverTm();
            //当前下载业务支持&&总控支持&&数据库有满足条件的文件 才会执行copy
            if (dbEnum.getCopy() == 1 && copyAll(dbEnum, empCode, fileName)) {
                return Result.success();
            }
            if (judgeConcurrency()) {
                return Result.fail(DownloadReEnum.DOWNLOAD_TOO_MUCH.getMsg());
            }
            //是否重复下载
            DownloadEntity downEntity = new DownloadEntity();
            downEntity.setUserCode(empCode);
            downEntity.setFileName(fileName);
            downEntity.setStartTm(TimeUtil.nowToStr(TimeFormatEnum.YYYY_2));
            downEntity.setStatus(DownloadReEnum.DOWNLOADING.getStatus());
            downEntity.setBusinessCopy(dbEnum.getCopy());
            downEntity.setSource(appEnum.getApp());
            //同一个人存在同名文件在下载则禁止下载，实时也限制
            if (!selectDowningRepeat(downEntity)) {
                return Result.fail(DownloadReEnum.DOWNLOADING.getMsg());
            }
            //存储信息
            sendDowningStatus(downEntity);
            //异步执行,指定超时时间,超时则更新超时状态，成功则更新相应的信息
            MulThreadUtil.excAsyncInTime(
                    () -> fileAndSendToOssPage(downEntity, callables, clazz, head, fileName, LocalDate.now().toString()),
                    this::sendDownedStatus,
                    () -> updateOverTmById(downEntity.getId()),
                    Duration.ofMinutes(getDownOverMinute())
            );
            return Result.success();
        } catch (Exception e) {
            log.error("executeRequest error ex:", e);
            return Result.fail();
        }
    }


    private <T, V, E> Result<E> executeRequest(Function<T, List<V>> function, T params, Class<V> clazz, List<List<String>> head, String empCode, String fileName, DownCopyEnum dbEnum, AppEnum appEnum) {
        try {
            //更新超时状态
            updateOverTm();
            //当前下载业务支持&&总控支持&&数据库有满足条件的文件 才会执行copy
            if (dbEnum.getCopy() == 1 && copyAll(dbEnum, empCode, fileName)) {
                return Result.success();
            }
            if (judgeConcurrency()) {
                return Result.fail(DownloadReEnum.DOWNLOAD_TOO_MUCH.getMsg());
            }
            //是否重复下载
            DownloadEntity downEntity = new DownloadEntity();
            downEntity.setUserCode(empCode);
            downEntity.setFileName(fileName);
            downEntity.setStartTm(TimeUtil.nowToStr(TimeFormatEnum.YYYY_2));
            downEntity.setStatus(DownloadReEnum.DOWNLOADING.getStatus());
            downEntity.setBusinessCopy(dbEnum.getCopy());
            downEntity.setSource(appEnum.getApp());
            //同一个人存在同名文件在下载则禁止下载，实时也限制
            if (!selectDowningRepeat(downEntity)) {
                return Result.fail(DownloadReEnum.DOWNLOADING.getMsg());
            }
            //存储信息
            sendDowningStatus(downEntity);
            //异步执行,指定超时时间,超时则更新超时状态，成功则更新相应的信息
            MulThreadUtil.excAsyncInTime(
                    params,
                    param -> fileAndSendToOss(downEntity, function, param, clazz, head, fileName),
                    this::sendDownedStatus,
                    () -> updateOverTmById(downEntity.getId()),
                    Duration.ofMinutes(getDownOverMinute())
            );
            return Result.success();
        } catch (Exception e) {
            log.error("executeRequest error params:{} ex:", params, e);
            return Result.fail();
        }
    }

    private <T, V, E> Result<E> executeRequest(Function<T, List<V>> function, T params, Class<V> clazz, List<List<String>> head, String empCode, String fileName, List<String> includeColumnNames, DownCopyEnum dbEnum) {
        try {
            //更新超时状态
            updateOverTm();
            //当前下载业务支持&&总控支持&&数据库有满足条件的文件 才会执行copy
            if (dbEnum.getCopy() == 1 && copyAll(dbEnum, empCode, fileName)) {
                return Result.success();
            }
            if (judgeConcurrency()) {
                return Result.fail(DownloadReEnum.DOWNLOAD_TOO_MUCH.getMsg());
            }
            //是否重复下载
            DownloadEntity downEntity = new DownloadEntity();
            downEntity.setUserCode(empCode);
            downEntity.setFileName(fileName);
            downEntity.setStartTm(TimeUtil.nowToStr(TimeFormatEnum.YYYY_2));
            downEntity.setStatus(DownloadReEnum.DOWNLOADING.getStatus());
            downEntity.setBusinessCopy(dbEnum.getCopy());
            //同一个人存在同名文件在下载则禁止下载，实时也限制
            if (!selectDowningRepeat(downEntity)) {
                return Result.fail(DownloadReEnum.DOWNLOADING.getMsg());
            }
            //存储信息
            sendDowningStatus(downEntity);
            //异步执行,指定超时时间,超时则更新超时状态，成功则更新相应的信息
            MulThreadUtil.excAsyncInTime(
                    params,
                    param -> fileAndSendToOss(downEntity, function, param, clazz, head, fileName, includeColumnNames),
                    this::sendDownedStatus,
                    () -> updateOverTmById(downEntity.getId()),
                    Duration.ofMinutes(getDownOverMinute())
            );
            return Result.success();
        } catch (Exception e) {
            log.error("executeRequest error params:{} ex:", params, e);
            return Result.fail();
        }
    }


    /**
     * 异步执行
     *
     * @param downEntity ；
     * @param function   ；
     * @param params     ；
     * @param clazz      ；
     * @param head       ；
     * @param fileName   ；不带扩展名
     * @param container  ；
     * @param <V>        ；
     * @param <T>        ；
     * @return ；
     */
    public <V, T> DownloadEntity fileAndSendToOss(DownloadEntity downEntity, Function<T, List<V>> function, T params, Class<V> clazz, List<List<String>> head, String fileName, List<String> includeColumnNames, String container) {
        ZipFile zipFile = null;
        try {
            zipFile = new ZipFile();
            List<V> list = function.apply(params);
            //int maxFileLine = commonConf.getFileMaxLine() <= 0 ? MAX_LINE : commonConf.getFileMaxLine();
            //int fileExpireTm = commonConf.getFileExpireTm() <= 0 ? EXPIRE_TM : commonConf.getFileExpireTm();
            int maxFileLine = getFileMaxLine();
            int fileExpireTm = getFileExpireTm();
            //生成压缩流
            if (CollectionUtils.isEmpty(list)) {
                downEntity.setStatus(DownloadReEnum.DOWNLOAD_NODATA.getStatus());
                throw new Exception(DownloadReEnum.DOWNLOAD_NODATA.getMsg());
            }
            List<List<V>> lists = splitList(list, maxFileLine);
            for (int i = 0; i < lists.size(); i++) {
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                if (clazz == null) {
                    EasyExcel.write(out).sheet("数据").head(head)
//                            .registerWriteHandler()
//                            .registerWriteHandler()
                            .doWrite(lists.get(i));
                } else {
                    EasyExcel.write(out).sheet("数据").head(clazz)
                            .includeColumnFiledNames(includeColumnNames)
                            .doWrite(lists.get(i));
                }
                zipFile.add(out, FileUtils.addXLSX(FileUtils.addMark(fileName, i)));
            }
            FileUtils.setNull(lists);

            //存oss
            zipFile.close();
            OssUploadFileEntity res = sendToOSS(container, fileName, zipFile.getResultByte());
            if (res == null) {
                throw new RuntimeException("sendToOSS error 文件上传失败");
            }
            //存数据库
            downEntity.setUrl(res.getFilePathUrl());
            downEntity.setContainerName(container);
            downEntity.setOssFileName(res.getFileName());
            downEntity.setEndTm(TimeUtil.nowToStr(TimeFormatEnum.YYYY_2));
            downEntity.setSpendTm(TimeUtil.calBetween(downEntity.getStartTm(), downEntity.getEndTm(), TimeFormatEnum.YYYY_2, ChronoUnit.SECONDS));
            downEntity.setExpireTm(TimeUtil.ldtToStr(TimeUtil.trans(downEntity.getEndTm(), TimeFormatEnum.YYYY_2).plusDays(fileExpireTm), TimeFormatEnum.YYYY_1));
            downEntity.setSize(zipFile.getResultOut().size() / 1024);
            downEntity.setLine(list.size());
            downEntity.setStatus(DownloadReEnum.DOWNLOAD_SUC.getStatus());
        } catch (Exception e) {
            if (downEntity.getStatus() == null || DownloadReEnum.DOWNLOADING.getStatus().equals(downEntity.getStatus())) {
                downEntity.setStatus(DownloadReEnum.DOWNLOAD_EXC.getStatus());
            }
            downEntity.setEndTm(TimeUtil.nowToStr(TimeFormatEnum.YYYY_2));
            log.error("downRequest thenAccept fail params:{} error:", params, e);
        } finally {
            zipFile.close();
            zipFile.setNull();
            FileUtils.setNull(zipFile);
        }
        return downEntity;
    }

    public <V, T> DownloadEntity fileAndSendToOss(DownloadEntity downEntity, Function<T, List<V>> function, T params, Class<V> clazz, List<List<String>> head, String fileName, String container) {
        ZipFile zipFile = null;
        try {
            zipFile = new ZipFile();
            List<V> list = function.apply(params);
            //int maxFileLine = commonConf.getFileMaxLine() <= 0 ? MAX_LINE : commonConf.getFileMaxLine();
            //int fileExpireTm = commonConf.getFileExpireTm() <= 0 ? EXPIRE_TM : commonConf.getFileExpireTm();
            int maxFileLine = getFileMaxLine();
            int fileExpireTm = getFileExpireTm();
            //生成压缩流
            if (CollectionUtils.isEmpty(list)) {
                downEntity.setStatus(DownloadReEnum.DOWNLOAD_NODATA.getStatus());
                throw new Exception(DownloadReEnum.DOWNLOAD_NODATA.getMsg());
            }
            List<List<V>> lists = splitList(list, maxFileLine);
            for (int i = 0; i < lists.size(); i++) {
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                if (clazz == null) {
                    EasyExcel.write(out).sheet("数据").head(head)
//                            .registerWriteHandler()
//                            .registerWriteHandler()
                            .doWrite(lists.get(i));
                } else {
                    EasyExcel.write(out).sheet("数据").head(clazz).doWrite(lists.get(i));
                }
                zipFile.add(out, FileUtils.addXLSX(FileUtils.addMark(fileName, i)));
            }
            FileUtils.setNull(lists);

            //存oss
            zipFile.close();
            OssUploadFileEntity res = sendToOSS(container, fileName, zipFile.getResultByte());
            if (res == null) {
                throw new RuntimeException("sendToOSS error 文件上传失败");
            }
            //存数据库
            downEntity.setUrl(res.getFilePathUrl());
            downEntity.setContainerName(container);
            downEntity.setOssFileName(res.getFileName());
            downEntity.setEndTm(TimeUtil.nowToStr(TimeFormatEnum.YYYY_2));
            downEntity.setSpendTm(TimeUtil.calBetween(downEntity.getStartTm(), downEntity.getEndTm(), TimeFormatEnum.YYYY_2, ChronoUnit.SECONDS));
            downEntity.setExpireTm(TimeUtil.ldtToStr(TimeUtil.trans(downEntity.getEndTm(), TimeFormatEnum.YYYY_2).plusDays(fileExpireTm), TimeFormatEnum.YYYY_1));
            downEntity.setSize(zipFile.getResultOut().size() / 1024);
            downEntity.setLine(list.size());
            downEntity.setStatus(DownloadReEnum.DOWNLOAD_SUC.getStatus());
        } catch (Exception e) {
            if (downEntity.getStatus() == null || DownloadReEnum.DOWNLOADING.getStatus().equals(downEntity.getStatus())) {
                downEntity.setStatus(DownloadReEnum.DOWNLOAD_EXC.getStatus());
            }
            downEntity.setEndTm(TimeUtil.nowToStr(TimeFormatEnum.YYYY_2));
            log.error("downRequest thenAccept fail params:{} error:", params, e);
        } finally {
            zipFile.close();
            zipFile.setNull();
            FileUtils.setNull(zipFile);
        }
        return downEntity;
    }


    /**
     * 异步执行(分页)
     *
     * @param downEntity ；
     * @param callables  ；每个callable就是一个分页
     * @param clazz      ；
     * @param head       ；
     * @param fileName   ；不带扩展名
     * @param container  ；
     * @param <V>        ；
     * @return ；
     */
    public <V> DownloadEntity fileAndSendToOssPage(DownloadEntity downEntity, List<Callable<List<V>>> callables, Class<V> clazz, List<List<String>> head, String fileName, String container) {
        ZipFile zipFile = null;
        try {
            zipFile = new ZipFile();
            int size = 0;
            for (int i = 0; i < callables.size(); i++) {
                List<V> list = callables.get(i).call();
                size += (list == null ? 0 : list.size());
                //生成压缩流
                if (CollectionUtils.isEmpty(list)) {
                    downEntity.setStatus(DownloadReEnum.DOWNLOAD_NODATA.getStatus());
                    throw new Exception(DownloadReEnum.DOWNLOAD_NODATA.getMsg());
                }
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                if (clazz == null) {
                    EasyExcel.write(out).sheet("数据").head(head)
                            .registerWriteHandler(new WriteHandler() {
                            })
//                            .registerWriteHandler()
                            .doWrite(list);
                } else {
                    EasyExcel.write(out).sheet("数据").head(clazz).doWrite(list);
                }
                zipFile.add(out, FileUtils.addXLSX(FileUtils.addMark(fileName, i)));
                FileUtils.setNull(list);
            }

            //存oss
            zipFile.close();
            OssUploadFileEntity res = sendToOSS(container, fileName, zipFile.getResultByte());
            if (res == null) {
                throw new RuntimeException("sendToOSS error 文件上传失败");
            }
            //存数据库
            downEntity.setUrl(res.getFilePathUrl());
            downEntity.setContainerName(container);
            downEntity.setOssFileName(res.getFileName());
            downEntity.setEndTm(TimeUtil.nowToStr(TimeFormatEnum.YYYY_2));
            downEntity.setSpendTm(TimeUtil.calBetween(downEntity.getStartTm(), downEntity.getEndTm(), TimeFormatEnum.YYYY_2, ChronoUnit.SECONDS));
            int fileExpireTm = getFileExpireTm();
            downEntity.setExpireTm(TimeUtil.ldtToStr(TimeUtil.trans(downEntity.getEndTm(), TimeFormatEnum.YYYY_2).plusDays(fileExpireTm), TimeFormatEnum.YYYY_1));
            downEntity.setSize(zipFile.getResultOut().size() / 1024);
            downEntity.setLine(size);
            downEntity.setStatus(DownloadReEnum.DOWNLOAD_SUC.getStatus());
        } catch (Exception e) {
            if (downEntity.getStatus() == null || DownloadReEnum.DOWNLOADING.getStatus().equals(downEntity.getStatus())) {
                downEntity.setStatus(DownloadReEnum.DOWNLOAD_EXC.getStatus());
            }
            downEntity.setEndTm(TimeUtil.nowToStr(TimeFormatEnum.YYYY_2));
            log.error("downRequest thenAccept fail error:", e);
        } finally {
            if (zipFile != null) {
                zipFile.close();
                zipFile.setNull();
                FileUtils.setNull(zipFile);
            }
        }
        return downEntity;
    }


    /**
     * 异步执行( 分页执行)
     *
     * @param downEntity ；
     * @param function   ；
     * @param params     ；
     * @param clazz      ；
     * @param head       ；
     * @param fileName   ；不带扩展名
     * @param container  ；
     * @param <V>        ；
     * @param <T>        ；
     * @return ；
     */
    public <V, T> DownloadEntity fileAndSendToOssPage(DownloadEntity downEntity, Function<T, List<V>> function, T params, Class<V> clazz,
                                                      List<List<String>> head, String fileName, String sheetName, String container) {
        ZipFile zipFile = null;
        try {
            zipFile = new ZipFile();
            //int maxFileLine = commonConf.getFileMaxLine() <= 0 ? MAX_LINE : commonConf.getFileMaxLine();
            //int fileExpireTm = commonConf.getFileExpireTm() <= 0 ? EXPIRE_TM : commonConf.getFileExpireTm();
            int maxFileLine = getFileMaxLine();
            int fileExpireTm = getFileExpireTm();
            int count = 0;
            int i = 0;
            int total = 0;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ExcelWriter excelWriter = EasyExcel.write(out).build();
            WriteSheet writeSheet;
            if (clazz == null) {
                writeSheet = EasyExcel.writerSheet(sheetName).head(head)
//                        .registerWriteHandler()
//                        .registerWriteHandler()
                        .build();
            } else {
                writeSheet = EasyExcel.writerSheet(sheetName).head(clazz).build();
            }
            JSONObject object = (JSONObject) JSON.toJSON(params);
            int pageNum = object.getIntValue("pageNo");
            int pageSize = object.getIntValue("pageSize");
            while (true) {
                List<V> list = function.apply(params);
                //生成压缩流
                if (CollectionUtils.isEmpty(list) && pageNum == 1) {
                    downEntity.setStatus(DownloadReEnum.DOWNLOAD_NODATA.getStatus());
                    throw new Exception(DownloadReEnum.DOWNLOAD_NODATA.getMsg());
                }
                if (CollectionUtils.isEmpty(list)) {
                    break;
                }
                total += list.size();
                count += list.size();
                if (list.size() < pageSize) {
                    ++i;
                    excelWriter.write(list, writeSheet);
                    excelWriter.finish();
                    zipFile.add(out, FileUtils.addXLSX(FileUtils.addMark(fileName, i)));
                    break;
                }
                pageNum++;
                Field field = params.getClass().getDeclaredField("pageNo");
                field.setAccessible(true);
                field.set(params, pageNum);
                excelWriter.write(list, writeSheet);
                if (count >= maxFileLine) {
                    ++i;
                    count = 0;
                    excelWriter.finish();
                    zipFile.add(out, FileUtils.addXLSX(FileUtils.addMark(fileName, i)));
                    out = new ByteArrayOutputStream();
                    excelWriter = EasyExcel.write(out).build();
                    if (clazz == null) {
                        writeSheet = EasyExcel.writerSheet(sheetName).head(head)
//                                .registerWriteHandler()
//                                .registerWriteHandler()
                                .build();
                    } else {
                        writeSheet = EasyExcel.writerSheet(sheetName).head(clazz).build();
                    }
                }
            }
            //存oss
            zipFile.close();
            OssUploadFileEntity res = sendToOSS(container, fileName, zipFile.getResultByte());
            if (res == null) {
                throw new RuntimeException("sendToOSS error 文件上传失败");
            }
            //存数据库
            downEntity.setUrl(res.getFilePathUrl());
            downEntity.setContainerName(container);
            downEntity.setOssFileName(res.getFileName());
            downEntity.setEndTm(TimeUtil.nowToStr(TimeFormatEnum.YYYY_2));
            downEntity.setSpendTm(TimeUtil.calBetween(downEntity.getStartTm(), downEntity.getEndTm(), TimeFormatEnum.YYYY_2, ChronoUnit.SECONDS));
            downEntity.setExpireTm(TimeUtil.ldtToStr(TimeUtil.trans(downEntity.getEndTm(), TimeFormatEnum.YYYY_2).plusDays(fileExpireTm), TimeFormatEnum.YYYY_1));
            downEntity.setSize(zipFile.getResultOut().size() / 1024);
            downEntity.setLine(total);
            downEntity.setStatus(DownloadReEnum.DOWNLOAD_SUC.getStatus());
        } catch (Exception e) {
            if (downEntity.getStatus() == null || DownloadReEnum.DOWNLOADING.getStatus().equals(downEntity.getStatus())) {
                downEntity.setStatus(DownloadReEnum.DOWNLOAD_EXC.getStatus());
            }
            downEntity.setEndTm(TimeUtil.nowToStr(TimeFormatEnum.YYYY_2));
            log.error("downRequest thenAccept fail params:{} error:", params, e);
        } finally {
            zipFile.close();
            zipFile.setNull();
            FileUtils.setNull(zipFile);
        }
        return downEntity;
    }

    public <V, T> DownloadEntity fileAndSendToOssPage(DownloadEntity downEntity, Function<T, List<V>> function, T params, Class<V> clazz, List<List<String>> head, String fileName, String sheetName) {
        return fileAndSendToOssPage(downEntity, function, params, clazz, head, fileName, sheetName, LocalDate.now().toString());
    }

    public <V, T> DownloadEntity fileAndSendToOss(DownloadEntity downEntity, Function<T, List<V>> function, T params, Class<V> clazz, List<List<String>> head, String fileName) {
        return fileAndSendToOss(downEntity, function, params, clazz, head, fileName, LocalDate.now().toString());
    }

    public <V, T> DownloadEntity fileAndSendToOss(DownloadEntity downEntity, Function<T, List<V>> function, T params, Class<V> clazz, List<List<String>> head, String fileName, List<String> includeColumnNames) {
        return fileAndSendToOss(downEntity, function, params, clazz, head, fileName, includeColumnNames, LocalDate.now().toString());
    }


    private <T> List<List<T>> splitList(List<T> list, Integer maxLine) {
        ArrayList<List<T>> result = new ArrayList<>();
        int total = (list.size() / maxLine) + (list.size() % maxLine == 0 ? 0 : 1);
        for (int j = 0; j < total; j++) {
            result.add(list.subList(j * maxLine, Math.min(maxLine * (j + 1), list.size())));
        }
        return result;
    }

    /**
     * 文件上传至oss
     *
     * @return ;
     */
    private OssUploadFileEntity sendToOSS(String containerName, String fileName, byte[] file) {
        int fileExpireTm = getFileExpireTm();
        UploadFileStreamEntity req = new UploadFileStreamEntity();
        req.setContainer(containerName);
        req.setBaseFileName(fileName);
        req.setFile(file);
        req.setDuration(Duration.ofDays(fileExpireTm));
        req.setFileType(FileTypeEnum.ZIP.getCode());

        if (rpcAsyncDownloadService != null) {
            return postObjToOss(req);
        }
        return HttpUtil.executePostObj(commonConf.getBaseOssUrl(), JSON.toJSONString(req), OssUploadFileEntity.class, HttpUtil.getTimeOut(getConnectTimeOut(), getFileSocketTimeOut()));
    }

    private OssUploadFileEntity postObjToOss(UploadFileStreamEntity req) {
        return HttpUtil.executePostObj(getBaseOssUrl(), JSON.toJSONString(req), OssUploadFileEntity.class, HttpUtil.getTimeOut(getConnectTimeOut(), getFileSocketTimeOut()));
    }

    private Integer getConnectTimeOut() {
        if (rpcAsyncDownloadService != null) {
            return rpcAsyncDownloadService.getOssConfigDto().getConnectTimeOut();
        }
        return commonConf.getConnectTimeOut();
    }

    private Integer getFileSocketTimeOut() {
        if (rpcAsyncDownloadService != null) {
            return rpcAsyncDownloadService.getOssConfigDto().getFileSocketTimeOut();
        }
        return commonConf.getFileSocketTimeOut();
    }

    private int getFileMaxLine() {
        if (rpcAsyncDownloadService != null) {
            Integer fileMaxLine = rpcAsyncDownloadService.getOssConfigDto().getFileMaxLine();
            return fileMaxLine <= 0 ? MAX_LINE : fileMaxLine;
        }
        return commonConf.getFileMaxLine() <= 0 ? MAX_LINE : commonConf.getFileMaxLine();
    }

    private int getFileExpireTm() {
        if (rpcAsyncDownloadService != null) {
            Integer fileExpireTm = rpcAsyncDownloadService.getOssConfigDto().getFileExpireTm();
            return fileExpireTm <= 0 ? EXPIRE_TM : fileExpireTm;
        }
        return commonConf.getFileExpireTm() <= 0 ? EXPIRE_TM : commonConf.getFileExpireTm();
    }

    private String getBaseOssUrl() {
        if (rpcAsyncDownloadService != null) {
            return rpcAsyncDownloadService.getOssConfigDto().getBaseOssUrl();
        }
        return commonConf.getBaseOssUrl();
    }

    private int getDownOverMinute() {
        if (rpcAsyncDownloadService != null) {
            Integer overMinute = rpcAsyncDownloadService.getOssConfigDto().getDownOverMinute();
            return overMinute == null ? downOverMinute : overMinute;
        }
        return commonConf.getDownOverMinute() == null ? downOverMinute : commonConf.getDownOverMinute();
    }

    /**
     * 添加下载信息
     *
     * @return ;
     */
    private boolean sendDowningStatus(DownloadEntity req) {
        if (rpcAsyncDownloadService != null) {
            Result<Long> longResult = rpcAsyncDownloadService.sendDowningStatus(req);
            if (longResult == null) {
                return false;
            }
            if (longResult.isSuccess()) {
                req.setId(longResult.getObj());
            }
            return longResult.isSuccess();
        }

        Result<Object> result = HttpUtil.postObj(commonConf.getSendDowningStatusUrl(), null, Result.class);
        if (result == null) {
            return false;
        }
        if (result.isSuccess()) {
            long id = Long.parseLong(String.valueOf(result.getObj()));
            req.setId(id);
        }
        return result.isSuccess();
    }


    private boolean sendDownedStatus(DownloadEntity req) {
        if (rpcAsyncDownloadService != null) {
            return rpcAsyncDownloadService.sendDownedStatus(req);
        }

        Result result = HttpUtil.postObj(commonConf.getSendDownedStatusUrl(), JSON.toJSONString(req), Result.class);
        if (result == null) {
            return false;
        }
        return result.isSuccess();
    }

    private Boolean copyAll(DownCopyEnum dbEnum, String empCode, String fileName) {
        if (rpcAsyncDownloadService != null) {
            return rpcAsyncDownloadService.copyAll(dbEnum, empCode, fileName);
        }
        if (!commonConf.getIsCopy() || dbEnum != DownCopyEnum.COPY) {
            return false;
        }
        DownloadEntity down = new DownloadEntity();
        Integer copyMinute = commonConf.getCopyMinute();
        String copyStartTm = TimeUtil.ldtToStr(LocalDateTime.now().minusMinutes(copyMinute), TimeFormatEnum.YYYY_2);
        down.setStartTm(copyStartTm);
        down.setFileName(fileName);
        down.setUserCode(empCode);
        Result<Object> result = HttpUtil.postObj(commonConf.getCopyAllUrl(), JSON.toJSONString(down), Result.class);
        if (result == null) {
            return false;
        }
        return result.isSuccess();
    }

    /**
     * @return ;
     */
    private boolean judgeConcurrency() {
        if (rpcAsyncDownloadService != null) {
            return rpcAsyncDownloadService.judgeConcurrency();
        }
        Result<Object> result = HttpUtil.postObj(commonConf.getDowningCntUrl(), null, Result.class);
        if (result == null) {
            return false;
        }
        int count = Integer.parseInt(String.valueOf(result.getObj()));
        return count >= (commonConf.getDowningCnt() == null ? downingCnt : commonConf.getDowningCnt());
    }


    private boolean selectDowningRepeat(DownloadEntity downEntity) {
        if (rpcAsyncDownloadService != null) {
            return rpcAsyncDownloadService.selectDowningRepeat(downEntity);
        }
        Result<Object> result = HttpUtil.postObj(commonConf.getSelectDowningRepeatUrl(), JSON.toJSONString(downEntity), Result.class);
        if (result == null) {
            return false;
        }
        return result.isSuccess();
    }


    private boolean updateOverTmById(Long id) {
        if (rpcAsyncDownloadService != null) {
            return rpcAsyncDownloadService.updateOverTmById(id);
        }
        DownloadEntity down = new DownloadEntity();
        down.setId(id);
        Result<Object> result = HttpUtil.postObj(commonConf.getUpdateOverTmByIdUrl(), JSON.toJSONString(down), Result.class);
        if (result == null) {
            return false;
        }
        return result.isSuccess();
    }


    private boolean updateOverTm() {
        if (rpcAsyncDownloadService != null) {
            return rpcAsyncDownloadService.updateOverTm();
        }
        Result<Object> result = HttpUtil.postObj(commonConf.getUpdateOverTmUrl(), null, Result.class);
        if (result == null) {
            return false;
        }
        return result.isSuccess();
    }
}


